#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include <queue>
#include <time.h>

#define NUM 8
template <class T>

class BlockQueue
{
public:
    BlockQueue(int cap = NUM) : cap_(cap)
    {
        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&full, NULL);
        pthread_cond_init(&empty, NULL);
    }

    void pushdata(const T &data)
    {
        LockQueue();
        while (IsFull())//为满
        {
            NotifyConsume();//产生一个empty信号，
            std::cout << "queue is full,we are waiting consuming!" << std::endl;
            ProductWait();//等到full信号才行动
        }
        q.push(data);
        UnLockQueue();
    }

    void popdata(T &data)
    {
        LockQueue();
        while (IsEmpty()) // 为空
        {
            NotifyProduct();//产生一个full信号
            std::cout << "queue is empty,we are waiting producting!" << std::endl;
            ConsumeWait();//等到empty信号才行的
        }
        data=q.front();
        q.pop();
        UnLockQueue();
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&full);
        pthread_cond_destroy(&empty);
    }

private:
    void LockQueue()
    {
        pthread_mutex_lock(&mutex);
    }

    void UnLockQueue()
    {
        pthread_mutex_unlock(&mutex);
    }

    void ProductWait()
    {
        pthread_cond_wait(&full, &mutex);
    }

    void ConsumeWait()
    {
        pthread_cond_wait(&empty, &mutex);
    }

    void NotifyProduct()
    {
        pthread_cond_signal(&full);
    }

    void NotifyConsume()
    {
        pthread_cond_signal(&empty);
    }

    bool IsEmpty()
    {
        if (q.size() == 0)
        {
            return true;
        }
        return false;
    }

    bool IsFull()
    {
        if (q.size() == cap_)
        {
            return true;
        }
        return false;
    }

private:
    int cap_;
    pthread_mutex_t mutex;
    pthread_cond_t full;
    pthread_cond_t empty;
    std::queue<T> q;
};

void *product(void *arg)
{
    BlockQueue<int> *bqp = (BlockQueue<int> *)arg;
    srand((unsigned long)time(NULL)); // 随机数
    while (1)
    {
        int data = rand() % 1024;
        bqp->pushdata(data);
        std::cout << "we push a data in queue ,the data is " << data << std::endl;
    }
}

void *consume(void *arg)
{
    BlockQueue<int> *bqp = (BlockQueue<int> *)arg;
    int data;
    while (1)
    {
        bqp->popdata(data);
        std::cout << "comsumer done once,the data is " << data << std::endl;
    }
}
int main(void)
{
    BlockQueue<int> bq;
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, product, (void *)&bq);
    pthread_create(&tid2, NULL, consume, (void *)&bq);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}
